<?php
/**
 * Core class of the framework
 */
class C
{
	/**
	 * @var array list of static variables
	 * @sealso C::__callstatic
	 */
	public static $variables = array();
	/**
	 * @var array list of path aliases
	 * @seealso C::addPathAlias
	 */
	public static $pathAliases = array();

	/**
	 * Adds static variable to class
	 * @param string $name
	 * @param mixed $value
	 */
	public static function addStaticVariable($name, $value)
	{
		self::$variables[$name] = $value;
	}

	/**
	 * Adds path alias
	 *
	 * @param $alias
	 * @param $path
	 *
	 * Example: C::addPathAlias('bootstrap', 'app.components.bootstrap')
	 */
	public static function addPathAlias($alias, $path)
	{
		self::$pathAliases[$alias] = $path;
	}

	/**
	 * This method is invoked when the static method of the class is called
	 *
	 * @param $name
	 * @param $arguments
	 * @return mixed
	 */
	public static function __callstatic($name, $arguments)
	{
		return self::$variables[$name];
	}

	/**
	 * Runs application
	 */
	public static function init()
	{
		self::addStaticVariable('log', new CLog());
		self::addStaticVariable('db', new CDb());
		self::addStaticVariable('request', new CRequest());
		self::addStaticVariable('app', new CApplication());

		self::app()->run();
	}

	/**
	 * Resolves virtual file path
	 *
	 * @param $path
	 * @return string absolute file system path
	 *
	 * Examples:
	 *
	 * C::resolvePath('app.views.layouts') is equal to C::resolvePath('views.layouts')
	 * C::resolvePath('c') returns path to "framework" folder
	 * you can also use aliases:
	 * C::addPathAlias('bootstrap', 'app.components.bootstrap')
	 * C::resolvePath('bootstrap.components')
	 */
	public static function resolvePath($path)
	{
		$pathComponents = explode('.', $path);
		if (!count($pathComponents))
			return '';

		$pathRoot = APP_PATH;
		if ($pathComponents[0] == 'app')
		{
			array_shift($pathComponents);
		}
		elseif ($pathComponents[0] == 'c')
		{
			$pathRoot = C_PATH;
			array_shift($pathComponents);
		}

		if (count($pathComponents) && in_array($pathComponents[0], array_keys(self::$pathAliases)))
		{
			$aliasComponents = explode('.', self::$pathAliases[$pathComponents[0]]);
			unset($pathComponents[0]);

			if (count($pathComponents))
			{
				foreach ($pathComponents as $component)
					$aliasComponents[] = $component;
			}

			$pathComponents = $aliasComponents;
		}

		$result = $pathRoot.implode('/', $pathComponents);

		if (mb_substr($result, mb_strlen($result, 'UTF-8') - 1, 1, 'UTF-8') != '/')
			$result = $result.'/';

		return $result;
	}

	/**
	 * Includes whole directory
	 *
	 * @param $path real path to directory
	 * @param bool $subDirectories if true then function will work recursively
	 * @param mixed $except list of files that will not be included
	 * if $subDirectories equal to true then it will work only for the root folder
	 * @throws Exception
	 *
	 * Examples:
	 *
	 * C::includeDir('classes', true, 'some.php')
	 * C::includeDir(C_PATH.'classes', false, array('some.php', 'some2.php'))
	 */
	public static function includeDir($path, $subDirectories = true, $except = array())
	{
		if (!is_array($except))
			$except = array($except);

		$fullPath = $path;

		if (!file_exists($fullPath))
			throw new Exception('Не удается найти папку "'.$fullPath.'"');

		$dir = opendir($fullPath);

		while ($file = readdir($dir))
		{
			if (!in_array($file, array('.', '..')) && (empty($except) || (!empty($except) && !in_array($file, $except))))
			{
				$fullFileName = $fullPath.'/'.$file;

				if (is_dir($fullFileName))
				{
					if ($subDirectories)
						self::includeDir($fullFileName, $subDirectories);
				}
				else
					include $fullFileName;
			}
		}

		closedir($dir);
	}
}
